// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
#define CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_

#include <stdint.h>

#include <list>
#include <map>

#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "content/common/media/video_capture.h"
#include "content/common/video_capture.mojom.h"
#include "media/base/video_frame.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/bindings/binding.h"

namespace content {

// VideoCaptureImpl represents a capture device in renderer process. It provides
// an interface for clients to command the capture (Start, Stop, etc), and
// communicates back to these clients e.g. the capture state or incoming
// captured VideoFrames. VideoCaptureImpl is created in the main Renderer thread
// but otherwise operates on |io_task_runner_|, which is usually the IO thread.
class CONTENT_EXPORT VideoCaptureImpl : public mojom::VideoCaptureObserver {
public:
    explicit VideoCaptureImpl(media::VideoCaptureSessionId session_id);
    ~VideoCaptureImpl() override;

    // Stop/resume delivering video frames to clients, based on flag |suspend|.
    void SuspendCapture(bool suspend);

    // Start capturing using the provided parameters.
    // |client_id| must be unique to this object in the render process. It is
    // used later to stop receiving video frames.
    // |state_update_cb| will be called when state changes.
    // |deliver_frame_cb| will be called when a frame is ready.
    void StartCapture(int client_id,
        const media::VideoCaptureParams& params,
        const VideoCaptureStateUpdateCB& state_update_cb,
        const VideoCaptureDeliverFrameCB& deliver_frame_cb);

    // Stop capturing. |client_id| is the identifier used to call StartCapture.
    void StopCapture(int client_id);

    // Requests that the video capturer send a frame "soon" (e.g., to resolve
    // picture loss or quality issues).
    void RequestRefreshFrame();

    // Get capturing formats supported by this device.
    // |callback| will be invoked with the results.
    void GetDeviceSupportedFormats(const VideoCaptureDeviceFormatsCB& callback);

    // Get capturing formats currently in use by this device.
    // |callback| will be invoked with the results.
    void GetDeviceFormatsInUse(const VideoCaptureDeviceFormatsCB& callback);

    media::VideoCaptureSessionId session_id() const { return session_id_; }

    void SetVideoCaptureHostForTesting(mojom::VideoCaptureHost* service)
    {
        video_capture_host_for_testing_ = service;
    }

private:
    friend class VideoCaptureImplTest;
    friend class MockVideoCaptureImpl;

    // Carries a shared memory for transferring video frames from browser to
    // renderer.
    class ClientBuffer;

    // Contains information about a video capture client, including capture
    //  parameters callbacks to the client.
    struct ClientInfo;
    using ClientInfoMap = std::map<int, ClientInfo>;

    using BufferFinishedCallback = base::Callback<void(double consumer_resource_utilization)>;

    // mojom::VideoCaptureObserver implementation.
    void OnStateChanged(mojom::VideoCaptureState state) override;
    void OnBufferCreated(int32_t buffer_id,
        mojo::ScopedSharedBufferHandle handle) override;
    void OnBufferReady(int32_t buffer_id,
        media::mojom::VideoFrameInfoPtr info) override;
    void OnBufferDestroyed(int32_t buffer_id) override;

    // Sends an IPC message to browser process when all clients are done with the
    // buffer.
    void OnClientBufferFinished(int buffer_id,
        const scoped_refptr<ClientBuffer>& buffer,
        double consumer_resource_utilization);

    void StopDevice();
    void RestartCapture();
    void StartCaptureInternal();

    void OnDeviceSupportedFormats(
        const VideoCaptureDeviceFormatsCB& callback,
        const media::VideoCaptureFormats& supported_formats);
    void OnDeviceFormatsInUse(
        const VideoCaptureDeviceFormatsCB& callback,
        const media::VideoCaptureFormats& formats_in_use);

    // Tries to remove |client_id| from |clients|, returning false if not found.
    bool RemoveClient(int client_id, ClientInfoMap* clients);

    mojom::VideoCaptureHost* GetVideoCaptureHost();

    // Called (by an unknown thread) when all consumers are done with a VideoFrame
    // and its ref-count has gone to zero.  This helper function grabs the
    // RESOURCE_UTILIZATION value from the |metadata| and then runs the given
    // callback, to trampoline back to the IO thread with the values.
    static void DidFinishConsumingFrame(
        const media::VideoFrameMetadata* metadata,
        const BufferFinishedCallback& callback_to_io_thread);

    // |device_id_| and |session_id_| are different concepts, but we reuse the
    // same numerical value, passed on construction.
    const int device_id_;
    const int session_id_;

    // |video_capture_host_| is an IO-thread InterfacePtr to a remote service
    // implementation and is created by binding |video_capture_host_info_|,
    // unless a |video_capture_host_for_testing_| has been injected.
    mojom::VideoCaptureHostPtrInfo video_capture_host_info_;
    mojom::VideoCaptureHostPtr video_capture_host_;
    mojom::VideoCaptureHost* video_capture_host_for_testing_;

    mojo::Binding<mojom::VideoCaptureObserver> observer_binding_;

    // Buffers available for sending to the client.
    using ClientBufferMap = std::map<int32_t, scoped_refptr<ClientBuffer>>;
    ClientBufferMap client_buffers_;

    ClientInfoMap clients_;
    ClientInfoMap clients_pending_on_restart_;

    // Video format requested by the client to this class via StartCapture().
    media::VideoCaptureParams params_;

    // First captured frame reference time sent from browser process side.
    base::TimeTicks first_frame_ref_time_;

    VideoCaptureState state_;

    base::ThreadChecker io_thread_checker_;

    // WeakPtrFactory pointing back to |this| object, for use with
    // media::VideoFrames constructed in OnBufferReceived() from buffers cached
    // in |client_buffers_|.
    base::WeakPtrFactory<VideoCaptureImpl> weak_factory_;

    DISALLOW_COPY_AND_ASSIGN(VideoCaptureImpl);
};

} // namespace content

#endif // CONTENT_RENDERER_MEDIA_VIDEO_CAPTURE_IMPL_H_
